/*
 * Copyright 2000 the original author or authors.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "WtInProcSession.h"
#include "WtUtil.h"
#include "WtMtTable.h"
#include "WtTableUtil.h"
#include "WtTable.h"

Tcl_HashTable *wtTableSessions;
Tcl_Mutex wtTableSessionsLock;

/* Initialize the session ID */

int WtInitProcSessionId(WtProcSession *ps, Tcl_Interp *interp)
{
  int ok = 0;
  WtSession *s;

  if (ps->id) {
    ok = 1;
  } else {
    s = WtGetAssocSession(interp);
    if (ps->id = WtGetInitialSessionId(s, &ps->isAutoId, interp)) {
      Tcl_IncrRefCount(ps->id);
      ok = 1;
    }
  }

  return ok;
}

/* Get the session ID */

Tcl_Obj *WtGetProcSessionId(WtProcSession *ps, Tcl_Interp *interp)
{
  return WtInitProcSessionId(ps, interp) ? ps->id : NULL;
}

/* Set the session ID. Closes the current session. */

int WtSetProcSessionId(WtProcSession *ps, Tcl_Obj *id, Tcl_Interp *interp)
{
  int ok = 0;

  if (!Tcl_GetCharLength(id)) {
    Tcl_AppendResult(interp, "Session ID string is invalid.", NULL);
  } else if (ps->id && !strcmp(WtToString(ps->id), WtToString(id))) {
    ok = 1;
  } else if (!ps->isOpen || WtCloseProcSession(ps, interp)) {
    if (ps->id) {
      Tcl_DecrRefCount(ps->id);
    }
    ps->id = id;
    Tcl_IncrRefCount(ps->id);
    ps->isAutoId = 0;
    ok = 1;
  }

  return ok;
}

/* Open the session */

int WtOpenProcSession(WtProcSession *ps, Tcl_Interp *interp)
{
  int ok = 0;

  if (ps->isOpen) {
    ok = 1;
  } else if (WtInitProcSessionId(ps, interp)) {
    /* Open it */
    ps->isOpen = 1;
    ok = 1;
  }

  return ok;
}

/* Close the session */

int WtCloseProcSession(WtProcSession *ps, Tcl_Interp *interp)
{
  int ok = 0;

  if (!ps->isOpen) {
    ok = 1;
  } else {
    /* Close it */
  }

  return ok;
}

/* Delete a session */

int WtDeleteProcSession(WtProcSession *ps, Tcl_Obj *id, Tcl_Interp *interp)
{
  int ok = 1;

  if (id) {
    if (!Tcl_GetCharLength(id)) {
      Tcl_AppendResult(interp, "Session ID string is invalid.", NULL);
      ok = 0;
    }
  } else if (!(id = WtGetProcSessionId(ps, interp))) {
    ok = 0;
  }

  if (ok) {
    /* Delete it */
    if (ps->id && !strcmp(WtToString(id), WtToString(ps->id))) {
      if (ps->isOpen) {
        ok = WtCloseProcSession(ps, interp);
      }
    }
  }

  return ok;
}

/* Delete stale sessions */

int WtSweepProcSessions(WtProcSession *ps, Tcl_Interp *interp)
{
  WtSession *s = WtGetAssocSession(interp);
  Tcl_Obj *maxIdleTime = Tcl_NewIntObj(s->maxIdleTime);
  /* Sweep */
  return 0;
}

/* Finalize request */

int WtProcSessionFinalizeRequest(WtProcSession *ps, Tcl_Interp *interp)
{
  int ok = 1;
  WtSession *s;

  if (ps->isOpen) {
    s = WtGetAssocSession(interp);
    if (WtShouldSweepSessions(s, interp)) {
      ok = WtSweepProcSessions(ps, interp);
    }
    if (ok) {
      ok = WtSetSessionCookie(s, ps->id, interp, -1);
    }
  }

  return ok;
}

WtProcSessionStorage *WtGetProcSessionStorage(WtProcSession *ps,
  Tcl_Interp *interp)
{
  WtProcSessionStorage *st = NULL;
  return st;
}

WtProcSessionStorage *WtGetProcSessionStorageById(Tcl_Obj *id,
  Tcl_Interp *interp)
{
  return NULL;
}

void WtReleaseProcSessionStorage(WtProcSessionStorage *st, Tcl_Interp *interp)
{
  Tcl_DecrRefCount(st->mtTable);
}

/* In-process session command */

int WtProcSessionCmd(ClientData clientData, Tcl_Interp *interp,
    int objc, Tcl_Obj *const objv[])
{
  WtSession *s = WtGetAssocSession(interp);
  WtProcSession *ps;

  /* Initialize the session */

  if (!s->inProcSession) {
    s->inProcSession = WtNewProcSessionObj();
  }

  ps = WtProcSessionRep(s->inProcSession);
  return WtProcSessionCmdHelper(clientData, interp,
    ps, objc, objv);
}

/* Session command helper */

int WtProcSessionCmdHelper(ClientData clientData, Tcl_Interp *interp,
    WtProcSession *ps, int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;
  char *subCmd;

  if (objc < 2) {
    WtProcSessionCmdUsage(interp, objv[0]);
    return ret;
  }

  subCmd = Tcl_GetString(objv[1]);

  if (!strcmp(subCmd, "id")) {
    ret = WtProcSessionIdCmd(clientData, interp, ps, objc, objv);
  } else if (!strcmp(subCmd, "count")) {
    ret = WtProcSessionCountCmd(clientData, interp, ps, objc, objv);
  } else if (!strcmp(subCmd, "has")) {
    ret = WtProcSessionHasCmd(clientData, interp, ps, objc, objv);
  } else if (!strcmp(subCmd, "get")) {
    ret = WtProcSessionGetCmd(clientData, interp, ps, objc, objv);
  } else if (!strcmp(subCmd, "set")) {
    ret = WtProcSessionSetCmd(clientData, interp, ps, objc, objv);
  } else if (!strcmp(subCmd, "setDefault")) {
    ret = WtProcSessionSetDefaultCmd(clientData, interp, ps, objc, objv);
  } else if (!strcmp(subCmd, "keys")) {
    ret = WtProcSessionKeysCmd(clientData, interp, ps, objc, objv);
  } else if (!strcmp(subCmd, "values")) {
    ret = WtProcSessionValuesCmd(clientData, interp, ps, objc, objv);
  } else if (!strcmp(subCmd, "remove")) {
    ret = WtProcSessionRemoveCmd(clientData, interp, ps, objc, objv);
  } else if (!strcmp(subCmd, "removeList")) {
    ret = WtProcSessionRemoveListCmd(clientData, interp, ps, objc, objv);
  } else if (!strcmp(subCmd, "array")) {
    ret = WtProcSessionArrayCmd(clientData, interp, ps, objc, objv);
  } else if (!strcmp(subCmd, "items")) {
    ret = WtProcSessionItemsCmd(clientData, interp, ps, objc, objv);
  } else if (!strcmp(subCmd, "clear")) {
    ret = WtProcSessionClearCmd(clientData, interp, ps, objc, objv);
  } else if (!strcmp(subCmd, "isAccessed")) {
    ret = WtProcSessionIsAccessedCmd(clientData, interp, ps, objc, objv);
  } else if (!strcmp(subCmd, "isModified")) {
    ret = WtProcSessionIsModifiedCmd(clientData, interp, ps, objc, objv);
  } else if (!strcmp(subCmd, "touch")) {
    ret = WtProcSessionTouchCmd(clientData, interp, ps, objc, objv);
  } else if (!strcmp(subCmd, "status")) {
    ret = WtProcSessionStatusCmd(clientData, interp, ps, objc, objv);
  } else if (!strcmp(subCmd, "properties")) {
    ret = WtProcSessionPropertiesCmd(clientData, interp, ps, objc, objv);
  } else if (!strcmp(subCmd, "delete")) {
    ret = WtProcSessionDeleteCmd(clientData, interp, ps, objc, objv);
  } else if (!strcmp(subCmd, "sweep")) {
    ret = WtProcSessionSweepCmd(clientData, interp, ps, objc, objv);
  } else if (!strcmp(subCmd, "loadModule")) {
    ret = WtProcSessionLoadModuleCmd(clientData, interp, ps, objc, objv);
  } else if (!strcmp(subCmd, "unloadModule")) {
    ret = WtProcSessionUnloadModuleCmd(clientData, interp, ps, objc, objv);
  } else if (!strcmp(subCmd, "finalizeRequest")) {
    ret = WtProcSessionFinalizeCmd(clientData, interp, ps, objc, objv);
  } else {
    WtProcSessionCmdUsage(interp, objv[0]);
  }

  return ret;
}

/* Session command usage */

WtProcSessionCmdUsage(Tcl_Interp *interp, Tcl_Obj *cmd)
{
  char *cmdStr = Tcl_GetString(cmd);

  Tcl_AppendResult(interp,
    wtBadUsagePrefix2,
    cmdStr, " id\n",
    cmdStr, " count\n",
    cmdStr, " has key\n",
    cmdStr, " get key ?defaultValue?\n",
    cmdStr, " set key value\n",
    cmdStr, " setDefault key defaultValue\n",
    cmdStr, " keys\n",
    cmdStr, " values\n",
    cmdStr, " remove key\n",
    cmdStr, " removeList keys\n",
    cmdStr, " array arrayName\n",
    cmdStr, " items\n",
    cmdStr, " clear\n",
    cmdStr, " isModified\n",
    cmdStr, " commit\n",
    cmdStr, " clearCache\n",
    cmdStr, " status ?id?\n",
    cmdStr, " properties ?id?\n",
    cmdStr, " delete ?id?\n",
    cmdStr, " sweep\n",
    cmdStr, " finalizeRequest\n",
    NULL);
}

/* id command */

int WtProcSessionIdCmd(ClientData clientData, Tcl_Interp *interp,
    WtProcSession *ps, int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;

  if (objc != 2 && objc != 3) {
    Tcl_AppendResult(interp, wtBadUsagePrefix,
      WtToString(objv[0]), " ", WtToString(objv[1]), NULL);
    return ret;
  } else if (objc == 2) {
    Tcl_SetObjResult(interp, ps->id ? ps->id : WtNewString(NULL));
    ret = TCL_OK;
  } else if (WtSetProcSessionId(ps, objv[2], interp)) {
    Tcl_SetObjResult(interp, ps->id);
    ret = TCL_OK;
  }

  return ret;
}

/* count command */

int WtProcSessionCountCmd(ClientData clientData, Tcl_Interp *interp,
    WtProcSession *ps, int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;
  WtProcSessionStorage *st;

  if (objc != 2) {
    Tcl_AppendResult(interp, wtBadUsagePrefix,
      WtToString(objv[0]), " ", WtToString(objv[1]), NULL);
    return ret;
  } else if (st = WtGetProcSessionStorage(ps, interp)) {
    Tcl_SetObjResult(interp, Tcl_NewIntObj(WtMtTableSize(st->mtTable)));
    WtReleaseProcSessionStorage(st, interp);
    ret = TCL_OK;
  }

  return ret;
}

/* has command */

int WtProcSessionHasCmd(ClientData clientData, Tcl_Interp *interp,
    WtProcSession *ps, int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;
  WtProcSessionStorage *st;

  if (objc != 3) {
    Tcl_AppendResult(interp, wtBadUsagePrefix,
      WtToString(objv[0]), " ", WtToString(objv[1]), " key", NULL);
    return ret;
  } else if (st = WtGetProcSessionStorage(ps, interp)) {
    Tcl_SetObjResult(interp, Tcl_NewBooleanObj(
      WtMtTableHas(st->mtTable, objv[2])));
    WtReleaseProcSessionStorage(st, interp);
    ret = TCL_OK;
  }

  return ret;
}

/* get command */

int WtProcSessionGetCmd(ClientData clientData, Tcl_Interp *interp,
    WtProcSession *ps, int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;
  WtProcSessionStorage *st;
  Tcl_Obj *obj;

  if (objc != 3 && objc != 4) {
    Tcl_AppendResult(interp, wtBadUsagePrefix,
      WtToString(objv[0]), " ", WtToString(objv[1]), " key ?defaultValue?",
        NULL);
    return ret;
  } else if (st = WtGetProcSessionStorage(ps, interp)) {
    obj = WtMtTableGet(st->mtTable, objv[2]);
    if (obj) {
      Tcl_SetObjResult(interp, obj);
      ret = TCL_OK;
    } else if (objc > 3) {
      Tcl_SetObjResult(interp, objv[3]);
      ret = TCL_OK;
    } else {
      Tcl_AppendResult(interp,
        "No entry with key \"", Tcl_GetString(objv[2]),
        "\" exists, and no default value was specified.", NULL);
    }
    WtReleaseProcSessionStorage(st, interp);
  }

  return ret;
}

/* set command */

int WtProcSessionSetCmd(ClientData clientData, Tcl_Interp *interp,
    WtProcSession *ps, int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;
  WtProcSessionStorage *st;

  if (objc != 4) {
    Tcl_AppendResult(interp, wtBadUsagePrefix,
      WtToString(objv[0]), " ", WtToString(objv[1]), " key value", NULL);
    return ret;
  } else if (st = WtGetProcSessionStorage(ps, interp)) {
    WtMtTableSet(st->mtTable, objv[2], objv[3]);
    Tcl_SetObjResult(interp, objv[3]);
    WtReleaseProcSessionStorage(st, interp);
    ret = TCL_OK;
  }

  return ret;
}

/* setDefault command */

int WtProcSessionSetDefaultCmd(ClientData clientData, Tcl_Interp *interp,
    WtProcSession *ps, int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;
  Tcl_Obj *obj;
  WtProcSessionStorage *st;

  if (objc != 4) {
    Tcl_AppendResult(interp, wtBadUsagePrefix,
      WtToString(objv[0]), " ", WtToString(objv[1]), " key defaultValue",
        NULL);
    return ret;
  } else if (st = WtGetProcSessionStorage(ps, interp)) {
    obj = WtMtTableGet(st->mtTable, objv[2]);
    if (obj) {
      Tcl_SetObjResult(interp, obj);
    } else {
      WtMtTableSet(st->mtTable, objv[2], objv[3]);
      Tcl_SetObjResult(interp, objv[3]);
      ret = TCL_OK;
    }
    WtReleaseProcSessionStorage(st, interp);
  }

  return ret;
}

/* keys command */

int WtProcSessionKeysCmd(ClientData clientData, Tcl_Interp *interp,
    WtProcSession *ps, int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;
  Tcl_Obj *keys;
  WtProcSessionStorage *st;

  if (objc != 2) {
    Tcl_AppendResult(interp, wtBadUsagePrefix,
      WtToString(objv[0]), " ", WtToString(objv[1]), NULL);
  } else if (st = WtGetProcSessionStorage(ps, interp)) {
    if (WtMtTableKeys(st->mtTable, &keys, interp)) {
      Tcl_SetObjResult(interp, keys);
      ret = TCL_OK;
    }
    WtReleaseProcSessionStorage(st, interp);
  }

  return ret;
}

/* values command */

int WtProcSessionValuesCmd(ClientData clientData, Tcl_Interp *interp,
    WtProcSession *ps, int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;
  Tcl_Obj *vals;
  WtProcSessionStorage *st;

  if (objc != 2) {
    Tcl_AppendResult(interp, wtBadUsagePrefix,
      WtToString(objv[0]), " ", WtToString(objv[1]), NULL);
  } else if (st = WtGetProcSessionStorage(ps, interp)) {
    if (WtMtTableValues(st->mtTable, &vals, interp)) {
      Tcl_SetObjResult(interp, vals);
      ret = TCL_OK;
    }
    WtReleaseProcSessionStorage(st, interp);
  }

  return ret;
}

/* remove command */

int WtProcSessionRemoveCmd(ClientData clientData, Tcl_Interp *interp,
    WtProcSession *ps, int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;
  WtProcSessionStorage *st;

  if (objc != 3) {
    Tcl_AppendResult(interp, wtBadUsagePrefix,
      WtToString(objv[0]), " ", WtToString(objv[1]), " key", NULL);
  } else if (st = WtGetProcSessionStorage(ps, interp)) {
    WtMtTableRemove(st->mtTable, objv[2]);
    ret = TCL_OK;
    WtReleaseProcSessionStorage(st, interp);
  }

  return ret;
}

/* removeList command */

int WtProcSessionRemoveListCmd(ClientData clientData, Tcl_Interp *interp,
    WtProcSession *ps, int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR, listLen;
  WtProcSessionStorage *st;

  if (objc != 3) {
    Tcl_AppendResult(interp, wtBadUsagePrefix,
      WtToString(objv[0]), " ", WtToString(objv[1]), " list", NULL);
  } else {
    if (Tcl_ListObjLength(interp, objv[2], &listLen) != TCL_OK) {
      Tcl_AppendResult(interp,
        "Argument is not a list. Usage: ",
        WtToString(objv[0]), " ", WtToString(objv[1]),
        " list", NULL);
    } else if (st = WtGetProcSessionStorage(ps, interp)) {
      if (WtMtTableRemoveList(st->mtTable, objv[2], interp)) {
        ret = TCL_OK;
      }
      WtReleaseProcSessionStorage(st, interp);
    }
  }

  return ret;
}

/* array command */

int WtProcSessionArrayCmd(ClientData clientData, Tcl_Interp *interp,
    WtProcSession *ps, int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;
  WtProcSessionStorage *st;

  if (objc != 3) {
    Tcl_AppendResult(interp, wtBadUsagePrefix,
      WtToString(objv[0]), " ", WtToString(objv[1]), " arrayName",
      NULL);
  } else if (st = WtGetProcSessionStorage(ps, interp)) {
    /*
    if (WtMtTableToArray(st->mtTable, objv[2], 0)) {
      ret = TCL_OK;
    }
    */
    WtReleaseProcSessionStorage(st, interp);
  }

  return ret;
}

/* items command */

int WtProcSessionItemsCmd(ClientData clientData, Tcl_Interp *interp,
    WtProcSession *ps, int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;
  /*Tcl_Obj *tbl;*/
  WtProcSessionStorage *st;

  if (objc != 2) {
    Tcl_AppendResult(interp, wtBadUsagePrefix,
      WtToString(objv[0]), " ", WtToString(objv[1]), NULL);
  } else if (st = WtGetProcSessionStorage(ps, interp)) {
    /*
    if (tbl = WtMtTableItems(interp, st->mtTable)) {
      Tcl_SetObjResult(tbl);
      ret = TCL_OK;
    }
    */
    WtReleaseProcSessionStorage(st, interp);
  }

  return ret;
}

/* clear command */

int WtProcSessionClearCmd(ClientData clientData, Tcl_Interp *interp,
    WtProcSession *ps, int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;
  WtProcSessionStorage *st;

  if (objc != 2) {
    Tcl_AppendResult(interp, wtBadUsagePrefix,
      WtToString(objv[0]), " ", WtToString(objv[1]), NULL);
  } else if (st = WtGetProcSessionStorage(ps, interp)) {
    WtMtTableClear(st->mtTable);
    WtReleaseProcSessionStorage(st, interp);
    ret = TCL_OK;
  }

  return ret;
}

/* isAccessed command */

int WtProcSessionIsAccessedCmd(ClientData clientData, Tcl_Interp *interp,
    WtProcSession *ps, int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;

  if (objc != 2) {
    Tcl_AppendResult(interp, wtBadUsagePrefix,
      WtToString(objv[0]), " ", WtToString(objv[1]), NULL);
  } else {
    Tcl_SetObjResult(interp, Tcl_NewBooleanObj(ps->isOpen));
    ret = TCL_OK;
  }

  return ret;
}

/* isModified command */

int WtProcSessionIsModifiedCmd(ClientData clientData, Tcl_Interp *interp,
    WtProcSession *ps, int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;

  if (objc != 2) {
    Tcl_AppendResult(interp, wtBadUsagePrefix,
      WtToString(objv[0]), " ", WtToString(objv[1]), NULL);
  } else {
    Tcl_SetObjResult(interp, Tcl_NewBooleanObj(ps->isModified));
    ret = TCL_OK;
  }

  return ret;
}

/* touch command */

int WtProcSessionTouchCmd(ClientData clientData, Tcl_Interp *interp,
    WtProcSession *ps, int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;
  WtProcSessionStorage *st;

  if (objc != 2) {
    Tcl_AppendResult(interp, wtBadUsagePrefix,
      WtToString(objv[0]), " ", WtToString(objv[1]), NULL);
  } else if (st = WtGetProcSessionStorage(ps, interp)) {
    WtReleaseProcSessionStorage(st, interp);
    ret = TCL_OK;
  }

  return ret;
}

/* status command
 *
 * Result values:
 *
 *     "new" - The session is new and hasn't been saved.
 *     "active" - Session is active.
 *     "expired" - Session has expired.
 */

int WtProcSessionStatusCmd(ClientData clientData, Tcl_Interp *interp,
    WtProcSession *ps, int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR, done = 0;
  /*Tcl_Obj *id;*/
  WtProcSessionStorage *st;

  if (objc != 2 && objc != 3) {
    Tcl_AppendResult(interp, wtBadUsagePrefix,
      WtToString(objv[0]), " ", WtToString(objv[1]), " ?id?", NULL);
  } else if (ps->isAutoId) {
    Tcl_SetResult(interp, "new", NULL);
    ret = TCL_OK;
  } else if (st = WtGetProcSessionStorage(ps, interp)) {
    /* Check expiration */
    Tcl_SetResult(interp, "active", NULL);
    WtReleaseProcSessionStorage(st, interp);
    ret = TCL_OK;
  }

  return ret;
}

/* properties command */

int WtProcSessionPropertiesCmd(ClientData clientData, Tcl_Interp *interp,
    WtProcSession *ps, int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR, done = 0;
  Tcl_Obj *id, *tbl;
  WtProcSessionStorage *st;
  WtSession *s;

  if (objc != 2 && objc != 3) {
    Tcl_AppendResult(interp, wtBadUsagePrefix,
      WtToString(objv[0]), " ", WtToString(objv[1]), " ?id?", NULL);
  } else {
    id = objc > 2 ? objv[2] : WtGetProcSessionId(ps, interp);
    if (st = WtGetProcSessionStorageById(id, interp)) {
      tbl = WtNewTableObj();
      /* Add access time */
      WtTableSetStrToInt(tbl, "size", WtMtTableSize(st->mtTable));
      WtTableSetStrToObj(tbl, "id", id);
      s = WtGetAssocSession(interp);
      WtTableSetStrToObj(tbl, "clientValue", s->clientValue);
      WtTableSetStrToObj(tbl, "clientId",
        s->clientId ? s->clientId : WtNewString(NULL));
      WtTableSetStrToLong(tbl, "clientTimeStamp",
        (long)s->clientTimeStamp);
      Tcl_SetObjResult(interp, tbl);
      WtReleaseProcSessionStorage(st, interp);
      ret = TCL_OK;
    }
  }

  return ret;
}

/* delete command */

int WtProcSessionDeleteCmd(ClientData clientData, Tcl_Interp *interp,
    WtProcSession *ps, int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;

  if (objc != 3) {
    Tcl_AppendResult(interp, wtBadUsagePrefix,
      WtToString(objv[0]), " ", WtToString(objv[1]), " ?id?", NULL);
    return ret;
  } else if (WtDeleteProcSession(ps, NULL, interp)) {
    ret = TCL_OK;
  }

  return ret;
}

/* sweep command */

int WtProcSessionSweepCmd(ClientData clientData, Tcl_Interp *interp,
    WtProcSession *ps, int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;

  if (WtSweepProcSessions(ps, interp)) {
    ret = TCL_OK;
  }

  return ret;
}

/* load command */

int WtProcSessionLoadModuleCmd(ClientData clientData, Tcl_Interp *interp,
    WtProcSession *ps, int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;

  if (objc != 2) {
    Tcl_AppendResult(interp, wtBadUsagePrefix,
      WtToString(objv[0]), " ", WtToString(objv[1]), NULL);
  } else {
    /* Do nothing */
    ret = TCL_OK;
  }

  return ret;
}

/* unloadModule command */

int WtProcSessionUnloadModuleCmd(ClientData clientData, Tcl_Interp *interp,
    WtProcSession *ps, int objc, Tcl_Obj *const objv[])
{
  WtContext *w;
  int ret = TCL_ERROR;

  if (objc != 2) {
    Tcl_AppendResult(interp, wtBadUsagePrefix,
      WtToString(objv[0]), " ", WtToString(objv[1]), NULL);
  } else {
    if (WtCloseProcSession(ps, interp)) {
      ret = TCL_OK;
    }
    w = WtGetAssocContext(interp);
    Tcl_DecrRefCount(w->web->session.inProcSession);
    w->web->session.inProcSession = NULL;
  }

  return ret;
}

/* finalizeRequest command */

int WtProcSessionFinalizeCmd(ClientData clientData, Tcl_Interp *interp,
    WtProcSession *ps, int objc, Tcl_Obj *const objv[])
{
  int ret = TCL_ERROR;

  if (objc != 2) {
    Tcl_AppendResult(interp, wtBadUsagePrefix,
      WtToString(objv[0]), " ", WtToString(objv[1]), NULL);
    return ret;
  } else if (WtProcSessionFinalizeRequest(ps, interp)) {
    ret = TCL_OK;
  }

  return ret;
}

/* Create session commands */

void WtInitProcSessionCommands(Tcl_Interp *interp)
{
  Tcl_CreateObjCommand(interp,
    "::wt::session::inProcSession::inProcSessionHandler",
    WtProcSessionCmd, NULL, NULL);
}

/* Create a new table session */

Tcl_Obj *WtNewProcSessionObj()
{
  Tcl_Obj *obj = Tcl_NewObj();
  WtProcSession *ps = (WtProcSession *)ckalloc(sizeof(WtProcSession));

  ps->id = NULL;
  ps->isAutoId = 0;
  ps->isOpen = 0;
  ps->isModified = 0;

  obj->internalRep.twoPtrValue.ptr1 = ps;
  obj->internalRep.twoPtrValue.ptr2 = NULL;
  obj->typePtr = &WtProcSessionType;
  Tcl_InvalidateStringRep(obj);

  return obj;
}

/* Get the internal rep */

WtProcSession *WtProcSessionRep(Tcl_Obj *obj)
{
  return (WtProcSession *)obj->internalRep.twoPtrValue.ptr1;
}

/* Copy a WtProcSession */

static void WtProcSessionDup(Tcl_Obj *src, Tcl_Obj *dst)
{
  WtProcSession *srcRep = WtProcSessionRep(src);
  WtProcSession *dstRep = (WtProcSession *)ckalloc(sizeof(WtProcSession));

  if (srcRep->id) {
    dstRep->id = Tcl_DuplicateObj(srcRep->id);
    Tcl_IncrRefCount(dstRep->id);
  } else {
    dstRep->id = NULL;
  }

  dstRep->isAutoId = srcRep->isAutoId;
  dstRep->isOpen = srcRep->isOpen;
  dstRep->isModified = srcRep->isModified;
  dst->internalRep.twoPtrValue.ptr1 = dstRep;
  dst->internalRep.twoPtrValue.ptr2 = NULL;
  dst->typePtr = &WtProcSessionType;
}

/* Update the string rep */

static void WtProcSessionUpdateString(Tcl_Obj *obj)
{
  WtUpdateProtectedObjString(obj);
}

/* Convert an obj to a WtProcSession */

static int WtProcSessionSetFromAny(Tcl_Interp *interp, Tcl_Obj *obj)
{
  return WtSetProtectedObjFromAny(interp, obj, &WtProcSessionType);
}

/* Free a WtProcSession */

static void WtProcSessionFree(Tcl_Obj *obj)
{
  WtProcSession *ps = WtProcSessionRep(obj);
  if (ps->id) {
    Tcl_DecrRefCount(ps->id);
  }
  ckfree((char *)(ps));
}

/* Table session type */

struct Tcl_ObjType WtProcSessionType =
{
  "inProcessSession",
  WtProcSessionFree,
  WtProcSessionDup,
  WtProcSessionUpdateString,
  WtProcSessionSetFromAny
};
